home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
User's Choice Windows CD
/
User's Choice Windows CD (CMS Software)(1993).iso
/
utility3
/
wincap.zip
/
WINCAP.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-11-05
|
30KB
|
933 lines
/*
* Wincap.c
*
* Windows Screen Capture Utility
* Version 3.00
*
* Description:
* ------------
*
* Captures portions of the screen, specific windows, or the entire screen
* and saves it to a file or prints it. Uses DIBAPI functions to do most
* of the capture/printing/saving work. See the file DIBAPI.TXT for a
* description of the DIB api functions.
*
* Development Team: Mark Bader
* Patrick Schreiber
* Garrett McAuliffe
* Eric Flo
* Tony Claflin
*
* Written by Microsoft Product Support Services, Developer Support.
* Copyright (c) 1991 Microsoft Corporation. All rights reserved.
*/
#include <windows.h>
#include <string.h>
#include "wincap.h"
#include "dialogs.h"
#include "dibapi.h"
#include "errors.h"
char szAppName[20]; // Name of application - used in dialog boxes
/* Global variables */
HWND ghInst; /* Handle to instance */
HWND ghWndMain; /* Handle to main window */
FARPROC lpfnKeyHook; // Used in keyboard hook
FARPROC lpfnOldHook; // Used for keyboard hook
HWND hModelessDlg; // Handle to modeless dialog box
/* Macro to swap two values */
#define SWAP(x,y) ((x)^=(y)^=(x)^=(y))
/**************************************************************************
*
* WinMain()
*
* Entry point of our Application.
*
*************************************************************************/
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine,
int nCmdShow)
{
MSG msg;
WNDCLASS wndclass;
HWND hWnd;
strcpy(szAppName, "WinCap"); // Name of our App
hModelessDlg = NULL; // Set handle to modeless dialog to NULL because
// we haven't created it yet
if (!hPrevInstance)
{
wndclass.style = 0;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, "WINCAP");
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = COLOR_WINDOW + 1;
wndclass.lpszMenuName = (LPSTR)NULL;
wndclass.lpszClassName = (LPSTR)szAppName;
if (!RegisterClass(&wndclass))
return FALSE;
ghInst = hInstance; // Set Global variable
/*
* Create a main window for this application instance - but don't
* display it.
*/
hWnd = CreateWindow(szAppName, // Name of the window's class
"Screen Capture", // Text for window caption
WS_OVERLAPPEDWINDOW, // Window Style
CW_USEDEFAULT, // Default horizontal position
CW_USEDEFAULT, // Default vertical position
CW_USEDEFAULT, // Default width
CW_USEDEFAULT, // Default height
NULL, // Overlapped windows have no parent
NULL, // Use the window class menu
hInstance, // This instance owns this window
NULL); // Pointer (not used)
ghWndMain = hWnd; // Set global variable
/*
* We want to keep the window iconic, so let's make sure that it
* starts out iconic (by using SW_SHOWMINNOACTIVE), and we also
* trap the WM_QUERYOPEN message in our main message loop. These
* two things together will keep our window iconic.
*/
ShowWindow(hWnd, SW_SHOWMINNOACTIVE);
UpdateWindow(hWnd);
/*
* Set up the Keyboard hook for our hotkey
*/
lpfnKeyHook = MakeProcInstance((FARPROC)KeyboardHook, hInstance);
lpfnOldHook = SetWindowsHook(WH_KEYBOARD, lpfnKeyHook);
}
/*
* If another instance of our program is already running, let the
* user know about it then exit.
*/
if (hPrevInstance)
{
MessageBox(NULL, "WinCap is already running. "
"There is no need to invoke it twice.", szAppName,
MB_OK | MB_ICONHAND);
return FALSE;
}
/* Polling messages from event queue */
while (GetMessage(&msg, NULL, 0, 0))
{
if (hModelessDlg == NULL || !IsDialogMessage(hModelessDlg, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
/***********************************************************************
*
* KeyboardHook()
*
* This is the Keyboard Hook function which windows will call every
* time it gets a keyboard message. In this function, we check to
* see if the key pressed was Ctrl+Alt+F9, and if it is, we post
* the proper message to our main window which will call up the
* printscreen dialog box.
*
**********************************************************************/
DWORD FAR PASCAL KeyboardHook(int iCode, WORD wParam, LONG lParam)
{
if (iCode == HC_ACTION && wParam == VK_F9 && GetKeyState(VK_SHIFT) < 0 &&
GetKeyState(VK_CONTROL) < 0)
{
if (HIWORD (lParam) & 0x8000)
PostMessage(ghWndMain, WM_PRTSC, 0, 0L);
return TRUE;
}
else
return DefHookProc(iCode, wParam, lParam, &lpfnOldHook);
}
/***********************************************************************
*
* WndProc()
*
* This is our main window procedure. It receives all the messages destined
* for our application's main window.
*
**********************************************************************/
long FAR PASCAL WndProc(HWND hWnd, WORD wMessage, WORD wParam, LONG lParam)
{
/*
* The bNowPrinting variable is set to TRUE if we are in the middle of
* printing. This takes care of the case when the user presses the hotkey
* during capturing
*/
static BOOL bNowPrinting = FALSE;
static HANDLE hOptionStruct; // handle to pass OPTIONSTRUCT to dialog box
switch (wMessage)
{
case WM_CREATE:
{
HANDLE hSysMenu;
FARPROC lpfnDIALOGSMsgProc;
LPOPTIONSTRUCT lpOptionStruct;
/*
* Since we want our main window to stay iconized, the user will never
* see any menus we add to our main window, so instead let's add the
* menu items to the system menu, which can be called up by clicking
* once on the icon.
*/
hSysMenu = GetSystemMenu(hWnd, FALSE);
AppendMenu(hSysMenu, MF_SEPARATOR, 0, NULL);
AppendMenu(hSysMenu, MF_STRING, IDM_ABOUT, "&About WinCap...");
AppendMenu(hSysMenu, MF_STRING, IDM_CAPTURE, "Ca&pture Screen...");
/*
* Put up InfoBox to let user know that we loaded.
*/
lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC)InfoBoxDlgProc, ghInst);
DialogBox(ghInst, (LPSTR)"InfoBox", hWnd, lpfnDIALOGSMsgProc);
FreeProcInstance(lpfnDIALOGSMsgProc);
/*
* Allocate memory for an OPTIONSTRUCT. This structure will be used to
* pass data to and receive data from the Options dialog box.
*/
hOptionStruct = GlobalAlloc(GMEM_MOVEABLE, sizeof(OPTIONSTRUCT) + 5);
/*
* Now set up default options
*/
lpOptionStruct = (LPOPTIONSTRUCT)GlobalLock(hOptionStruct);
if (hOptionStruct)
{
lpOptionStruct->iOptionArea = IDC_SINGLEWINDOW;
lpOptionStruct->iOptionWindow = IDC_ENTIREWINDOW;
lpOptionStruct->iOptionDest = OPTION_PRINTER;
lstrcpy(lpOptionStruct->szFileName, (LPSTR)"c:\\capture.bmp");
lpOptionStruct->iOptionPrint = IDC_BESTFIT;
lpOptionStruct->iXScale = 1;
lpOptionStruct->iYScale = 2;
GlobalUnlock(hOptionStruct);
}
}
break;
case WM_PRTSC:
{
/*
* The WM_PRTSC message is one that we defined in our header file. This
* message is sent to us when the user wants to capture the screen, either
* by hitting the hotkey (see the KeyboardHook procedure above), by
* double-clicking on the icon caption (see WM_QUERYOPEN message case
* below), or by selecting the menu item "Capture Screen..." (see
* WM_SYSCOMMAND message case below).
*/
/* Check to see that we aren't already in the middle of printing.
* This could happen if the user presses our hotkey in the middle of
* one of our dialog boxes.
*/
if (bNowPrinting)
{
MessageBox(NULL, "Already capturing screen.", szAppName, MB_OK |
MB_ICONEXCLAMATION);
}
else
{
// Commence screen capture!
bNowPrinting = TRUE;
DoCapture(hOptionStruct);
bNowPrinting = FALSE;
}
} /* End WM_PRTSC case */
break;
case WM_QUERYOPEN:
/*
* This code makes the window stay iconic. The PostMessage() is here
* so that when user double-clicks on icon, we do the printscreen.
*/
PostMessage(hWnd, WM_PRTSC, 0, 0L);
return FALSE;
case WM_SYSCOMMAND:
switch (wParam)
{
case IDM_ABOUT:
/*
* Display "About" Box
*/
{
FARPROC lpfnDIALOGSMsgProc;
lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC)AboutDlgProc, ghInst);
DialogBox(ghInst, (LPSTR)"About", hWnd, lpfnDIALOGSMsgProc);
FreeProcInstance(lpfnDIALOGSMsgProc);
}
return 0;
case IDM_CAPTURE:
/*
* User selected "Capture Screen..." From the menu
*/
PostMessage(hWnd, WM_PRTSC, 0, 0L);
return 0;
}
return DefWindowProc(hWnd, wMessage, wParam, lParam);
break;
case WM_DESTROY:
/*
* Clean up
*/
UnhookWindowsHook(WH_KEYBOARD, lpfnKeyHook);
/*
* Free memory used for OPTIONSTRUCT
*/
GlobalFree(hOptionStruct);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, wMessage, wParam, lParam);
}
return 0L;
}
/***********************************************************************
*
* DoCapture()
*
* This procedure gets called when the user wants to capture the
* screen. This is where we actually bring up the proper dialog
* boxes and call the proper screen capture functions.
*
**********************************************************************/
void DoCapture(HANDLE hOptionStruct)
{
FARPROC lpfnDIALOGSMsgProc; // Pointer for dialog boxes
int nResult; // return codes from the dialog boxes
LPOPTIONSTRUCT lpOptionStruct; // Pointer to OPTIONSTRUCT
static HDIB hDib; // Handle to our captured screen DIB
char szWindowText[100]; // Text which tells what we captured
static HWND hWndCurrent;
/*
* Keep track of the active window so we can bring it to top
* later.
*/
hWndCurrent = GetActiveWindow();
/*
* Call up Options dialog box
*/
lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC)OptionsDlgProc, ghInst);
/*
* Pass the handle to our OPTIONSTRUCT to the dialog box in the 5th parameter
* to DialogBoxParam. This will be passed to our dialog box in the lParam
* WM_INITDIALOG message. Since the 5th parameter to DialogBoxParam()
* is 32 bits, and our handle is only 16 bits, let's put it in the
* LOWORD() of the parameter.
*/
nResult = DialogBoxParam(ghInst, (LPSTR)"Options", ghWndMain,
lpfnDIALOGSMsgProc, (DWORD)MAKELONG(hOptionStruct,
0));
FreeProcInstance(lpfnDIALOGSMsgProc);
/*
* If user presses OK (nResult == TRUE), then go on, otherwise don't go on
*/
if (nResult)
{
lpOptionStruct = (LPOPTIONSTRUCT)GlobalLock(hOptionStruct);
if (!lpOptionStruct)
return;
/*
* The structure should now contain:
* 1. iOptionArea - Specifies which area user wants to capture
* One of: IDC_SINGLEWINDOW
* IDC_ENTIRESCREEN
* IDC_PARTIALSCREEN
* 2. iOptionWindow - Specifies which portion of the window to capture -
* this will only contain valid data if the iOptionArea is set to
* IDC_SINGLEWINDOW. Will be one of:
* IDC_ENTIREWINDOW
* IDC_CLIENTAREAONLY
* IDC_CAPTIONBARONLY
* IDC_MENUBARONLY
* 3. iOptionDest - Bitfield which specifies the destination of the
* bitmap. Can be a logical combination of: OPTION_FILE and
* OPTION_PRINTER.
* 4. szFileName - string which contains the filename the user
* typed into as the name to save the bitmap to. This is only
* valid if the iOptionDest has the OPTION_FILE bit set.
* 5. iOptionPrint - Print Options. Will be one of:
* IDC_BESTFIT
* IDC_STRETCHTOPAGE
* IDC_SCALE
* 6. iXScale, iYScale - X and Y scaling factors for printing bitmap.
* These values are only valid if iOptionPrint is set to IDC_SCALE.
*/
switch (lpOptionStruct->iOptionArea)
{
case IDC_ENTIRESCREEN:
/*
* Copy Entire screen to DIB
*/
{
RECT rScreen; // Rect containing entire screen
HDC hDC; // DC to screen
MSG msg; // Message for the PeekMessage()
hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
rScreen.left = rScreen.top = 0;
rScreen.right = GetDeviceCaps(hDC, HORZRES);
rScreen.bottom = GetDeviceCaps(hDC, VERTRES);
strcpy(szWindowText, "Entire screen");
/* Bring the previous current window to the top of the Z-order.
* Wait until this application gets another message -- this allows
* the other windows (which were obscured before by the dialog
* box) to repaint themselves.
*/
BringWindowToTop(hWndCurrent);
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/*
* Call the DIB API function which captures the screen. This
* will automatically allocate enough space for the DIB, fill
* in the proper fields in the DIB header, and return us a
* pointer to the memory containing the header, colortable,
* and bits of the DIB.
*/
hDib = CopyScreenToDIB(&rScreen);
}
break;
case IDC_PARTIALSCREEN:
/*
* Copy user-selected portion of screen to DIB
*/
{
RECT rRubberBand; // Region to capture (screen coordinates)
/*
* Allow user to "rubberband" a section of the screen for
* us to capture
*/
RubberBandScreen(&rRubberBand);
strcpy(szWindowText, "User selected portion");
/*
* Call the DIB API function which captures the screen. This
* will automatically allocate enough space for the DIB, fill
* in the proper fields in the DIB header, and return us a
* pointer to the memory containing the header, colortable,
* and bits of the DIB.
*/
hDib = CopyScreenToDIB(&rRubberBand);
}
break;
case IDC_SINGLEWINDOW:
/*
* Allow the user to click on a single window to capture
*/
{
HWND hWndSelect;
HWND hWndDesktop;
WORD wOption;
/*
* Call function which lets user select a window
*/
hWndSelect = SelectWindow();
/*
* Check to see that they didn't try to capture desktop window
*/
hWndDesktop = GetDesktopWindow();
if (hWndSelect == hWndDesktop)
{
MessageBox(NULL, "Cannot capture Desktop window."
" Use 'Entire Screen' option to capture"
" the entire screen.", szAppName,
MB_ICONEXCLAMATION | MB_OK);
hDib = NULL;
break;
}
/*
* Check to see that the hWnd is not NULL
*/
if (!hWndSelect)
{
MessageBox(NULL, "Cannot capture that window!", szAppName,
MB_ICONEXCLAMATION | MB_OK);
hDib = NULL;
break;
}
/*
* Make sure it's not a hidden window. Hmm, capturing a hidden
* window would certainly be a cool trick, wouldn't it?
*/
if (!IsWindowVisible(hWndSelect))
{
MessageBox(NULL, "Window is not visible. Can't capture",
szAppName, MB_ICONEXCLAMATION | MB_OK);
hDib = NULL;
break;
}
// Move window which was selected to top of Z-order for
// the capture, and make it redraw itself
SetWindowPos(hWndSelect, NULL, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOSIZE
| SWP_NOMOVE);
UpdateWindow(hWndSelect);
/*
* Get the caption
*/
GetWindowText(hWndSelect, szWindowText, 100);
/*
* Convert our OPTIONSTRUCT options to DIB API options
*/
switch (lpOptionStruct->iOptionWindow)
{
case IDC_CLIENTAREAONLY:
wOption = PW_CLIENT;
break;
case IDC_ENTIREWINDOW:
default:
wOption = PW_WINDOW;
break;
}
/*
* Call the DIB API function which captures the screen. This
* will automatically allocate enough space for the DIB, fill
* in the proper fields in the DIB header, and return us a
* pointer to the memory containing the header, colortable,
* and bits of the DIB.
*/
hDib = CopyWindowToDIB(hWndSelect, wOption);
}
break;
default:
/*
* Oops, something went wrong
*/
MessageBox(NULL, "Invalid Return value from Options DialogBox",
szAppName, MB_ICONHAND | MB_OK);
break;
}
/*
* At this point, if hDib is NULL, then there was an error. We should
* have already taken care of informing the user of the error.
*/
if (!hDib)
{
GlobalUnlock(hOptionStruct);
DestroyDIB(hDib);
return;
}
/*
* Now, process the destination information (e.g. to printer or file)
*/
/*
* See if the user checked the checkbox "Send to File"
*/
if (lpOptionStruct->iOptionDest & OPTION_FILE)
{
WORD wReturn;
FARPROC lpfnDIALOGSMsgProc;
// First, call up a modeless dialog box which tells that we are saving
// this to a file...
if (!hModelessDlg)
{
lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC)SavingDlgProc,
ghInst);
hModelessDlg = CreateDialogParam(ghInst, (LPSTR)"Saving",
ghWndMain, lpfnDIALOGSMsgProc, (
DWORD)(LPSTR)lpOptionStruct->
szFileName);
}
/*
* Call DIB API function which saves dib to file
*/
wReturn = SaveDIB(hDib, (LPSTR)lpOptionStruct->szFileName);
DestroyWindow(hModelessDlg);
hModelessDlg = NULL;
if (wReturn)
MessageBox(NULL, "Error saving file", szAppName,
MB_ICONEXCLAMATION | MB_OK);
FreeProcInstance(lpfnDIALOGSMsgProc);
}
/*
* See if the user checked the box "Print to printer"
*/
if (lpOptionStruct->iOptionDest & OPTION_PRINTER)
{
WORD wReturn;
WORD wOption;
WORD wX = 0, wY = 0;
switch (lpOptionStruct->iOptionPrint)
{
case IDC_STRETCHTOPAGE:
wOption = PW_STRETCHTOPAGE;
break;
case IDC_SCALE:
wOption = PW_SCALE;
wX = lpOptionStruct->iXScale;
wY = lpOptionStruct->iYScale;
break;
default:
case IDC_BESTFIT:
wOption = PW_BESTFIT;
break;
}
/*
* Send the bitmap to the printer, using the specified options
*/
wReturn = PrintDIB(hDib, wOption, wX, wY, (LPSTR)szWindowText);
if (wReturn)
{
DIBError(wReturn);
}
}
/*
* Clean up -- deallocate memory used for DIB
*/
GlobalUnlock(hOptionStruct);
DestroyDIB(hDib);
}
}
/***********************************************************************
*
* SelectWindow()
*
* This function allows the user to select a window on the screen. The
* cursor is changed to a custom cursor, then the user clicks on the title
* bar of a window to capture, and the handle to this window is returned.
*
**********************************************************************/
HWND SelectWindow()
{
HCURSOR hOldCursor; // Handle to old cursor
POINT pt; // Stores mouse position on a mouse click
HWND hWndClicked; // Window we clicked on
MSG msg;
/*
* Capture all mouse messages
*/
SetCapture(ghWndMain);
/*
* Load custom Cursor
*/
hOldCursor = SetCursor(LoadCursor(ghInst, "SELECT"));
/*
* Eat mouse messages until a WM_LBUTTONUP is encountered.
*/
for (;;)
{
WaitMessage();
if (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
{
if (msg.message == WM_LBUTTONUP)
{
/*
* Get mouse position
*/
pt.x = LOWORD(msg.lParam);
pt.y = HIWORD(msg.lParam);
/*
* Convert to screen coordinates
*/
ClientToScreen(ghWndMain, &pt);
/*
* Get Window that we clicked on
*/
hWndClicked = WindowFromPoint(pt);
/*
* If it's not a valid window, just return NULL
*/
if (!hWndClicked)
{
ReleaseCapture();
SetCursor(hOldCursor);
return NULL;
}
break;
}
}
else
continue;
}
ReleaseCapture();
SetCursor(hOldCursor);
return (hWndClicked);
}
/***********************************************************************
*
* RubberBandScreen()
*
* This function allows the user to rubber-band a portion of the screen.
* When the left button is released, the rect that the user selected
* (in screen coordinates) is returned in lpRect.
*
**********************************************************************/
void RubberBandScreen(LPRECT lpRect)
{
POINT pt; // Temporary POINT
MSG msg; // Used in our PeekMessage() loop
POINT ptOrigin; // Point where the user pressed left mouse button down
RECT rcClip; // Current selection
HDC hScreenDC; // DC to the screen (so we can draw on it)
HCURSOR hOldCursor; // Saves old cursor
BOOL bCapturing = FALSE; // TRUE if we are rubber-banding
hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
/*
* Make cursor our custom cursor
*/
hOldCursor = SetCursor(LoadCursor(NULL, IDC_CROSS));
/*
* Capture all mouse messages
*/
SetCapture(ghWndMain);
/* Eat mouse messages until a WM_LBUTTONUP is encountered. Meanwhile
* continue to draw a rubberbanding rectangle and display it's dimensions
*/
for (;;)
{
WaitMessage();
if (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
{
/*
* If the message is a WM_LBUTTONDOWN, begin drawing the
* rubber-band box.
*/
if (msg.message == WM_LBUTTONDOWN)
{
/*
* User pressed left button, initialize selection
* Set origin to current mouse position (in window coords)
*/
ptOrigin.x = LOWORD(msg.lParam);
ptOrigin.y = HIWORD(msg.lParam);
/*
* Convert to screen coordinates
*/
ClientToScreen(ghWndMain, &ptOrigin);
/*
* rcClip is the current rectangle that the user
* has selected. Since user just pressed left button down,
* initialize this rect very small
*/
rcClip.left = rcClip.right = ptOrigin.x;
rcClip.top = rcClip.bottom = ptOrigin.y;
NormalizeRect(&rcClip); // Make sure it is a normal rect
DrawSelect(hScreenDC, TRUE, &rcClip); // Draw the rubber-band box
bCapturing = TRUE;
}
/*
* Any messages that make it into the next statement are mouse
* messages, and we are capturing, so let's update the rubber-band
* box
*/
if (bCapturing)
{
DrawSelect(hScreenDC, FALSE, &rcClip); // erase old rubber-band
rcClip.left = ptOrigin.x; // Update rect with new mouse info
rcClip.top = ptOrigin.y;
pt.x = LOWORD(msg.lParam);
pt.y = HIWORD(msg.lParam);
/*
* Convert to screen coordinates
*/
ClientToScreen(ghWndMain, &pt);
rcClip.right = pt.x;
rcClip.bottom = pt.y;
NormalizeRect(&rcClip);
DrawSelect(hScreenDC, TRUE, &rcClip); // And draw the new rubber-band
}
// If the message is WM_LBUTTONUP, then we stop the selection
// process.
if (msg.message == WM_LBUTTONUP)
{
DrawSelect(hScreenDC, FALSE, &rcClip); // erase rubber-band
SetCursor(hOldCursor);
break;
}
}
else
continue;
}
ReleaseCapture();
DeleteDC(hScreenDC);
/*
* Assign rect user selected to lpRect parameter
*/
CopyRect(lpRect, &rcClip);
}
/****************************************************************************
*
* DrawSelect
*
* Draws the selected clip rectangle with its dimensions on the DC
* This code is taken from DIBVIEW.
*
****************************************************************************/
void DrawSelect(HDC hdc, // DC to draw on
BOOL fDraw, // TRUE to draw, FALSE to erase
LPRECT lprClip) // rect to draw
{
char sz[80];
DWORD dw;
int x, y, len, dx, dy;
HDC hdcBits;
HBITMAP hbm;
RECT rcClip;
rcClip = *lprClip;
if (!IsRectEmpty(&rcClip))
{
/* If a rectangular clip region has been selected, draw it */
PatBlt(hdc, rcClip.left, rcClip.top, rcClip.right - rcClip.left, 1,
DSTINVERT);
PatBlt(hdc, rcClip.left, rcClip.bottom, 1, -(rcClip.bottom - rcClip.top)
, DSTINVERT);
PatBlt(hdc, rcClip.right - 1, rcClip.top, 1, rcClip.bottom - rcClip.top,
DSTINVERT);
PatBlt(hdc, rcClip.right, rcClip.bottom - 1, -(rcClip.right -
rcClip.left), 1, DSTINVERT);
/* Format the dimensions string ...*/
wsprintf(sz, "%dx%d", rcClip.right - rcClip.left, rcClip.bottom -
rcClip.top);
len = lstrlen(sz);
/* ... and center it in the rectangle */
dw = GetTextExtent(hdc, sz, len);
dx = LOWORD (dw);
dy = HIWORD (dw);
x = (rcClip.right + rcClip.left - dx) / 2;
y = (rcClip.bottom + rcClip.top - dy) / 2;
hdcBits = CreateCompatibleDC(hdc);
SetTextColor(hdcBits, 0xFFFFFFL);
SetBkColor(hdcBits, 0x000000L);
/* Output the text to the DC */
if (hbm = CreateBitmap(dx, dy, 1, 1, NULL))
{
hbm = SelectObject(hdcBits, hbm);
ExtTextOut(hdcBits, 0, 0, 0, NULL, sz, len, NULL);
BitBlt(hdc, x, y, dx, dy, hdcBits, 0, 0, SRCINVERT);
hbm = SelectObject(hdcBits, hbm);
DeleteObject(hbm);
}
DeleteDC(hdcBits);
}
}
/****************************************************************************
* *
* FUNCTION : NormalizeRect(RECT *prc) *
* *
* PURPOSE : If the rectangle coordinates are reversed, swaps them *
* *
****************************************************************************/
void PASCAL NormalizeRect(LPRECT prc)
{
if (prc->right < prc->left)
SWAP(prc->right,prc->left);
if (prc->bottom < prc->top)
SWAP(prc->bottom,prc->top);
}